/******************************************************************************/
/*                                                                            */
/*                    H E A D E R   I N F O R M A T I O N                     */
/*                                                                            */
/******************************************************************************/

// Project Name                   : Ethernet Webserver Example for Helix_4
// File Name                      : w5100_nios2.c
// Prepared By                    : B. Singer
// Project Start                  : 2013-01-28


/******************************************************************************/
/*                                                                            */
/*                      C O P Y R I G H T   N O T I C E                       */
/*                                                                            */
/******************************************************************************/
/*
This file is a modified version of w5100.c, provided by Wiznet for use with
their Ethernet controller ICs. As such, this file inherits Wiznet's Copyright,
and their license. The file has been modified somewhat by Thin Layer Embedded
to work with the helix_4 and it's development systems.

Original Wiznet Copyright Notice
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	/*
	 * (c)COPYRIGHT
	 * ALL RIGHT RESERVED
	 *
	 * FileName : w5100.c
	 * Revision History :
	 * ----------	-------		------------------------------------------------
	 * 	Date			version	  	Description
	 * ----------	-------  	------------------------------------------------
	 * 01/25/2007	1.1			Bug is Fixed in the Indirect Mode
	 *							: Memory mapping error
	 * ----------	-------		------------------------------------------------
	 * 01/08/2008	1.2			Modification of Socket Command Part
	 *							: Check if the appropriately performed after writing Sn_CR
	 *
	 *							Modification of SPI Part
	 *							: SPI code changed by adding 'spi.h'.
	 *							: Change control type for SPI port from byte to bit.
	 * ----------	-------		------------------------------------------------
	 * 01/15/2008	1.3			Bug is Fixed in the pppinit() fuction.
	 *							: do not clear interrupt value, so fixed.
	 *
	 *		                   			Modification of ISR
	 *                   				: Do not exit ISR, if there is interrupt.
	 * ----------	-------		------------------------------------------------
	 * 03/21/2008	1.4			Modification of SetMR() function
	 *                   				: Use IINCHIP_WRITE() function in Direct or SPI mode.
	 * ----------	-------		------------------------------------------------
	 * 03/21/2008	1.5			Bug is Fixed in the pppinit() fuction.
	 *							: do not clear receive buffer, so fixed. +200903[bj] clear receive buffer
	 * ----------	-------		------------------------------------------------
	 * 03/13/2012	1.6			Added clearSUBR(), applySUBR() and modified setSUBR() functions
	 *							      because of the ARP errata.
	 * ----------	-------		------------------------------------------------
	 */
/*
Any modification, or code not by Seeed Studios in this file is released by
Thin Layer Embedded under the FreeBSD license.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	Copyright (c) 2013, Thin Layer Embedded Pty Ltd
	All rights reserved.

	Redistribution and use in source and binary forms, with or without
	modification, are permitted provided that the following conditions are met:

	1. Redistributions of source code must retain the above copyright notice, this
	   list of conditions and the following disclaimer.
	2. Redistributions in binary form must reproduce the above copyright notice,
	   this list of conditions and the following disclaimer in the documentation
	   and/or other materials provided with the distribution.

	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
	ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
	WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
	DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
	ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
	(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
	LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
	ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
	(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
	SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/******************************************************************************/
/*                                                                            */
/*                      I N   P L A I N   E N G L I S H                       */
/*                                                                            */
/******************************************************************************/
/*
These comments do not constitute legal advice, and Thin Layer Embedded is not
qualified to provide legal advice on software licensing. You should always
consult with qualified legal professionals to ensure that any information we
provide is correct and relevant to your situation and legal jurisdiction.

Wiznet have not specified any license for their code, and have not made a clear
copyright assertion. It appears they are happy for customers to use their code
and buy their chips. This would suggestyou  can do ANYTHING you want with this
code, but:
- there is no warranty and you can't hold Thin Layer Embedded liable for any
  damages.
- you need to include: "Copyright (c) 2013, Thin Layer Embedded Pty Ltd", only
  where you've used our code.

Thin Layer Embedded makes every effort to comply with software license
requirements and to acknowledge the owners of code wherever possible. If you see
a problem with our acknowledgements or licensing, please let us know immediately
so we can correct the issue: support@thin-layer-embedded.com.
*/

/******************************************************************************/
/*                                                                            */
/*                           H A R D W A R E                                  */
/*                                                                            */
/******************************************************************************/
/*
See system.h for system description

This software is designed to demonstrate the Seeed Studio 2.8" TFT Touch Screen
LCD on the helix_4 Multi-shield dev board. It will work on sockets 0, and 1.
On this code, socket 0 is used. You'll have to edit this code to change to
another socket, and edit the FPGA code by changing the MOD0 references to MOD1.

The 2.8" TFT Touch Screen LCD has a 4 wire SPI interface, with one additional
signal - D/C, mapped here as LCD_IS_DATA_N, which tells the LCD if the word
received is a data word, or a command. There is an sdcard that shares the SPI
bus, but the sdcard is not addressed in this code yet.

The shield pinout is:
D4 = SD_CARD_CS_N
D5 = LCD_CS_N
D6 = LCD_IS_DATA_N - D/C pin, high for command, low for data
D7 = BACKLIGHT_EN - used as a PWM for brightness control
D11 = LCD_MOSI  - master out, slave in - data FROM the FPGA
D12 = LCD_MISO  - master in, slave out - data TO the FPGA
D13 = LCD_SCLK  - serial clock
Other pins are not connected.

Inside the FPGA, we are running Nios2/e. To give it a bump, we are clocking at
75MHz. Note that if you use /s or /f versions performance drawing screen
primatives increases 100~200%. The backlight is driven with PWM at ~25kHz frame
rate, that is generated with an ordinary counter, and a comparison to a 6 bit
register to determine output.
*/

/******************************************************************************/
/*                                                                            */
/*                           D E S C R I P T I O N                            */
/*                                                                            */
/******************************************************************************/
/*
This code drives data into an SPI interface @ 10Mbit/s. The Wiznet IC decodes,
parses and handles the TCP/IP stack, MAC & Phy, and the only task for the
programmer here is the socket programming. The latter is not trivial, but can
be done on a single thread, saving the user the trouble of implmenting an OS.
*/

/******************************************************************************/
/*                                                                            */
/*                         C H A N G E  H I S T O R Y                         */
/*                                                                            */
/******************************************************************************/
/*
   Date          Version Description
   ------------------------------------------------------------------------
   28 Jan 2013   0.0     Established               B. Singer
*/
/******************************************************************************/
/*                                                                            */
/*                          T O D O   L I S T                                 */
/*                                                                            */
/******************************************************************************/
/*
1) todo: add fat file system, sd drivers, and load images from the sd card...
2) todo: write a bare metal SPI driver for performance. Current HAL driver is
      convenient and general purpose and seriously inefficient.
3) todo: comment and clean up of the whole code base
*/

/******************************************************************************/
/*                                                                            */
/*                      I N C L U D E   F I L E S                             */
/*                                                                            */
/******************************************************************************/
#include "w5100_nios2.h"
#include "altera_avalon_spi_w5100.h"
#include "types.h"
#include <system.h>
#include <alt_types.h>
#include <unistd.h>
#include "define.h"
#include "socket.h"

/******************************************************************************/
/*                                                                            */
/*                             D E F I N E S                                  */
/*                                                                            */
/******************************************************************************/

/******************************************************************************/
/*                              T Y P E D E F S                               */
/******************************************************************************/

/******************************************************************************/
/*                                                                            */
/*                   G L O B A L   V A R I A B L E S                          */
/*                                                                            */
/******************************************************************************/

/******************************************************************************/
/*                                                                            */
/*                          P R O T O T Y P E S                               */
/*                                                                            */
/******************************************************************************/

/******************************************************************************/
/*                                                                            */
/*                          F U N C T I O N S                                 */
/*                                                                            */
/******************************************************************************/
/*
 * Function: alt_avalon_spi_command
 *
 * Include: <altera_avalon_spi.h>
 *
 * Description: This function performs a control sequence on the SPI bus. It supports only SPI masters with
 *    data width less than or equal to 8 bits. A single call to this function writes a data buffer of
 *    arbitrary length to the mosiport, and then reads back an arbitrary amount of data from the
 *    misoport. The function performs the following actions:
 *       (1) Asserts the slave select output for the specified slave. The first slave select output is 0.
 *       (2) Transmits write_lengthbytes of data from wdatathrough the SPI interface,
 *           discarding the incoming data on the misoport.
 *       (3) Reads read_lengthbytes of data and stores the data into the buffer pointed to by
 *           read_data. The mosiport is set to zero during the read transaction.
 *       (4) De-asserts the slave select output, unless the flags field contains the value
 *           ALT_AVALON_SPI_COMMAND_MERGE. If you want to transmit from scattered buffers, call
 *           the function multiple times and specify the merge flag on all the accesses except the last.
 * To access the SPI bus from more than one thread, you must use a semaphore or mutex to
 *    ensure that only one thread is executing within this function at any time.
 *
 * Returns: The number of bytes stored in the read_databuffer.
 *
 * int alt_avalon_spi_command(alt_u32 base, alt_u32 slave, alt_u32 write_length, \
 *  	  const alt_u8* wdata, alt_u32 read_length, alt_u8* read_data, alt_u32 flags);
 */


void wiz_init( void );
alt_u8 wiz( alt_u8 opcode, alt_u16 addr, alt_u8 data );
alt_u8 IINCHIP_WRITE(alt_u16 addr, alt_u8 data);
alt_u8 IINCHIP_READ(alt_u16 addr);

/*********************************************************
* iinchip access function
*********************************************************/

extern alt_u16 wiz_read_buf(alt_u16 addr, alt_u8* buf,alt_u16 len);
extern alt_u16 wiz_write_buf(alt_u16 addr,alt_u8* buf,alt_u16 len);

extern void send_data_processing(SOCKET s, alt_u8 *data, alt_u16 len);
extern void recv_data_processing(SOCKET s, alt_u8 *data, alt_u16 len);
extern void read_data(SOCKET s, valt_u8 * src, valt_u8 * dst, alt_u16 len);
extern void write_data(SOCKET s, valt_u8 * src, valt_u8 * dst, alt_u16 len);

extern void iinchip_init(void); // reset iinchip
extern void sysinit(alt_u8 tx_size, alt_u8 rx_size); // setting tx/rx buf size
extern alt_u8 getISR(alt_u8 s);
extern void putISR(alt_u8 s, alt_u8 val);
extern alt_u16 getIINCHIP_RxMAX(alt_u8 s);
extern alt_u16 getIINCHIP_TxMAX(alt_u8 s);
extern alt_u16 getIINCHIP_RxMASK(alt_u8 s);
extern alt_u16 getIINCHIP_TxMASK(alt_u8 s);
extern alt_u16 getIINCHIP_RxBASE(alt_u8 s);
extern alt_u16 getIINCHIP_TxBASE(alt_u8 s);
extern void setGAR(alt_u8 * addr); // set gateway address
extern void setSUBR(alt_u8 * addr); // set subnet mask address
extern void clearSUBR(void);
extern void applySUBR(void);
extern void setSHAR(alt_u8 * addr); // set local MAC address
extern void setSIPR(alt_u8 * addr); // set local IP address
extern void setRTR(alt_u16 timeout); // set retry duration for data transmission, connection, closing ...
extern void setRCR(alt_u8 retry); // set retry count (above the value, assert timeout interrupt)
extern void setIMR(alt_u8 mask); // set interrupt mask.
extern void getGAR(alt_u8 * addr);
extern void getSUBR(alt_u8 * addr);
extern void getSHAR(alt_u8 * addr);
extern void getSIPR(alt_u8 * addr);
extern alt_u8 getIR( void );

extern void setSn_MSS(SOCKET s, alt_u16 Sn_MSSR0); // set maximum segment size
extern void setSn_PROTO(SOCKET s, alt_u8 proto); // set IP Protocol value using IP-Raw mode
extern alt_u8 getSn_IR(SOCKET s); // get socket interrupt status
void setSn_IR(SOCKET s, alt_u8 value);
extern alt_u8 getSn_SR(SOCKET s); // get socket status
extern alt_u16 getSn_TX_FSR(SOCKET s); // get socket TX free buf size
extern alt_u16 getSn_RX_RSR(SOCKET s); // get socket RX recv buf size
extern void setSn_DHAR(SOCKET s, alt_u8 * addr);
extern void setSn_DIPR(SOCKET s, alt_u8 * addr);
extern void setSn_DPORT(SOCKET s, alt_u8 * addr);
extern void getSn_DHAR(SOCKET s, alt_u8 * addr);
extern void getSn_DIPR(SOCKET s, alt_u8 * addr);
extern void getSn_DPORT(SOCKET s, alt_u8 * addr);
extern void setSn_TTL(SOCKET s, alt_u8 ttl);
extern void setMR(alt_u8 val);


#ifdef __DEF_IINCHIP_PPP__
   #include "../iinchip/md5.h"
#endif


//#if (__DEF_IINCHIP_BUS__ == __DEF_IINCHIP_SPI_MODE__)
//#include "spi.h"		//+2007113[jhpark]
//#endif

static alt_u8 I_STATUS[MAX_SOCK_NUM];
static alt_u16 SMASK[MAX_SOCK_NUM];           /**< Variable for Tx buffer MASK in each channel */
static alt_u16 RMASK[MAX_SOCK_NUM];           /**< Variable for Rx buffer MASK in each channel */
static alt_u16 SSIZE[MAX_SOCK_NUM];           /**< Max Tx buffer size by each channel */
static alt_u16 RSIZE[MAX_SOCK_NUM];           /**< Max Rx buffer size by each channel */
static alt_u16 SBUFBASEADDRESS[MAX_SOCK_NUM]; /**< Tx buffer base address by each channel */
static alt_u16 RBUFBASEADDRESS[MAX_SOCK_NUM]; /**< Rx buffer base address by each channel */
static alt_u8 SUBN_VAR[4];


void wiz_init( void )
{
	alt_u8 gar[4] = { 192, 168, 1, 1 };
	alt_u8 subr[4] = { 255, 255, 255, 0 };
	alt_u8 shar[6] = { 0x90, 0xA2, 0xDA ,0x0D, 0x9D, 0xAA };
	alt_u8 sipr[4] = { 192, 168, 1, 200 };

	iinchip_init();
	setGAR( &gar[0] );
	setSUBR( &subr[0] );
	applySUBR();
	setSHAR( &shar[0] );
	setSIPR( &sipr[0] );
}


alt_u8 wiz
(
	alt_u8 opcode,
	alt_u16 addr,
	alt_u8 data
)
{
	static alt_u8 write_data[4];
	static alt_u8 read_data[4];

	// pack command
	write_data[0] = opcode;
	write_data[1] = (alt_u8)( 0x00FF & ( addr >> 8 ) );
	write_data[2] = (alt_u8)( 0x00FF & addr );
	write_data[3] = data;

	// perform transaction
	alt_avalon_spi_command_w5100( SPI_0_BASE, WIZNET_SLAVE_POS, 0x4, &write_data[0], 0x4, &read_data[0], 0 );

	return read_data[3];
}


/**
@brief	This function writes the data into W5100 registers.
*/
alt_u8 IINCHIP_WRITE(alt_u16 addr,alt_u8 data)
{
// DIRECT MODE I/F
// not covered in this port to NIOS2
#if (__DEF_IINCHIP_BUS__ == __DEF_IINCHIP_DIRECT_MODE__)
	IINCHIP_ISR_DISABLE();
	*((vuint8*)(addr)) = data;
	IINCHIP_ISR_ENABLE();
#elif(__DEF_IINCHIP_BUS__ == __DEF_IINCHIP_INDIRECT_MODE__)	/* INDIRECT MODE I/F */
	   IINCHIP_ISR_DISABLE();
	   *((vuint8*)IDM_AR0) = (alt_u8)((addr & 0xFF00) >> 8);
	   *((vuint8*)IDM_AR1) = (alt_u8)(addr & 0x00FF);
	   *((vuint8*)IDM_DR)  = data;
	   IINCHIP_ISR_ENABLE();
#elif (__DEF_IINCHIP_BUS__ == __DEF_IINCHIP_SPI_MODE__)
	/*   IINCHIP_ISR_DISABLE();
	IINCHIP_SpiInit();

	//SPI MODE I/F
	IINCHIP_CSoff();                             // CS=0, SPI start

	IINCHIP_SpiSendData(0xF0);
	IINCHIP_SpiSendData((addr & 0xFF00) >> 8);
	IINCHIP_SpiSendData(addr & 0x00FF);
	IINCHIP_SpiSendData(data);

	IINCHIP_CSon();

	   IINCHIP_ISR_ENABLE();*/
	   wiz( WRITE, addr, data );
#else
	#error "unknown bus type"
#endif
	return 1;
}


/**
@brief	This function reads the value from W5100 registers.
*/
alt_u8 IINCHIP_READ(alt_u16 addr)
{
	alt_u8 data;

// DIRECT MODE I/F
// not covered in this port to NIOS2
#if (__DEF_IINCHIP_BUS__ == __DEF_IINCHIP_DIRECT_MODE__)
	IINCHIP_ISR_DISABLE();
	data = *((vuint8*)(addr));
	IINCHIP_ISR_ENABLE();
#elif(__DEF_IINCHIP_BUS__ == __DEF_IINCHIP_INDIRECT_MODE__)
	   IINCHIP_ISR_DISABLE();
	   *((vuint8*)IDM_AR0) = (alt_u8)((addr & 0xFF00) >> 8);
	   *((vuint8*)IDM_AR1) = (alt_u8)(addr & 0x00FF);
	   data = *((vuint8*)IDM_DR);
	   IINCHIP_ISR_ENABLE();

#elif (__DEF_IINCHIP_BUS__ == __DEF_IINCHIP_SPI_MODE__)
	   /*   IINCHIP_ISR_DISABLE();
	IINCHIP_SpiInit();
	IINCHIP_CSoff();                             // CS=0, SPI start

	IINCHIP_SpiSendData(0x0F);
	IINCHIP_SpiSendData((addr & 0xFF00) >> 8);
	IINCHIP_SpiSendData(addr & 0x00FF);


	IINCHIP_SpiSendData(0);
	IINCHIP_SpiRecvData();

	IINCHIP_CSon();                          	// SPI end
	   IINCHIP_ISR_ENABLE();*/
	data = wiz( READ, addr, 0x0 );
#else
	#error "unknown bus type"
#endif
	return data;
}


alt_u8 getISR(alt_u8 s)
{
	return I_STATUS[s];
}

void putISR(alt_u8 s, alt_u8 val)
{
   I_STATUS[s] = val;
}

alt_u16 getIINCHIP_RxMAX(alt_u8 s)
{
   return RSIZE[s];
}

alt_u16 getIINCHIP_TxMAX(alt_u8 s)
{
   return SSIZE[s];
}

alt_u16 getIINCHIP_RxMASK(alt_u8 s)
{
   return RMASK[s];
}

alt_u16 getIINCHIP_TxMASK(alt_u8 s)
{
   return SMASK[s];
}

alt_u16 getIINCHIP_RxBASE(alt_u8 s)
{
   return RBUFBASEADDRESS[s];
} 
alt_u16 getIINCHIP_TxBASE(alt_u8 s)
{
   return SBUFBASEADDRESS[s];
}

 /**
@brief	This function writes the data into W5100 registers.
// replaced by NIOS2 friendly function
alt_u8 IINCHIP_WRITE(alt_u16 addr,alt_u8 data)
{
// DIRECT MODE I/F
#if (__DEF_IINCHIP_BUS__ == __DEF_IINCHIP_DIRECT_MODE__)
	IINCHIP_ISR_DISABLE();
	*((vuint8*)(addr)) = data;
	IINCHIP_ISR_ENABLE();
#elif(__DEF_IINCHIP_BUS__ == __DEF_IINCHIP_INDIRECT_MODE__)	// INDIRECT MODE I/F
	   IINCHIP_ISR_DISABLE();
	   *((vuint8*)IDM_AR0) = (alt_u8)((addr & 0xFF00) >> 8);
	   *((vuint8*)IDM_AR1) = (alt_u8)(addr & 0x00FF);
	   *((vuint8*)IDM_DR)  = data;
	   IINCHIP_ISR_ENABLE();
#elif (__DEF_IINCHIP_BUS__ == __DEF_IINCHIP_SPI_MODE__)
	   IINCHIP_ISR_DISABLE();
	IINCHIP_SpiInit();
	
	//SPI MODE I/F
	IINCHIP_CSoff();                             // CS=0, SPI start

	IINCHIP_SpiSendData(0xF0);
	IINCHIP_SpiSendData((addr & 0xFF00) >> 8);
	IINCHIP_SpiSendData(addr & 0x00FF);
	IINCHIP_SpiSendData(data);

	IINCHIP_CSon();    

	   IINCHIP_ISR_ENABLE();   
#else
	#error "unknown bus type"
#endif
	return 1;
}
*/

/**
@brief	This function reads the value from W5100 registers.
// replaced by NIOS2 friendly function
alt_u8 IINCHIP_READ(alt_u16 addr)
{
	alt_u8 data;

// DIRECT MODE I/F

#if (__DEF_IINCHIP_BUS__ == __DEF_IINCHIP_DIRECT_MODE__)
	IINCHIP_ISR_DISABLE();
	data = *((vuint8*)(addr));
	IINCHIP_ISR_ENABLE();
#elif(__DEF_IINCHIP_BUS__ == __DEF_IINCHIP_INDIRECT_MODE__)   
	   IINCHIP_ISR_DISABLE();
	   *((vuint8*)IDM_AR0) = (alt_u8)((addr & 0xFF00) >> 8);
	   *((vuint8*)IDM_AR1) = (alt_u8)(addr & 0x00FF);
	   data = *((vuint8*)IDM_DR);
	   IINCHIP_ISR_ENABLE();
   
#elif (__DEF_IINCHIP_BUS__ == __DEF_IINCHIP_SPI_MODE__)
	   IINCHIP_ISR_DISABLE();
	IINCHIP_SpiInit();	
	IINCHIP_CSoff();                             // CS=0, SPI start

	IINCHIP_SpiSendData(0x0F);
	IINCHIP_SpiSendData((addr & 0xFF00) >> 8);
	IINCHIP_SpiSendData(addr & 0x00FF);
	
	
	IINCHIP_SpiSendData(0);
	data = IINCHIP_SpiRecvData();

	IINCHIP_CSon();                          	// SPI end
	   IINCHIP_ISR_ENABLE();
#else
	#error "unknown bus type"
#endif
	return data;
}
*/

/**
@brief	This function writes into W5100 memory(Buffer)
*/ 
alt_u16 wiz_write_buf(alt_u16 addr,alt_u8* buf,alt_u16 len)
{
#if (__DEF_IINCHIP_BUS__ == __DEF_IINCHIP_DIRECT_MODE__)
	IINCHIP_ISR_DISABLE();
   	memcpy((alt_u8 *)addr, buf, len);
	IINCHIP_ISR_ENABLE();
#elif (__DEF_IINCHIP_BUS__ == __DEF_IINCHIP_INDIRECT_MODE__)
	   alt_u16 idx = 0;
	   IINCHIP_ISR_DISABLE();
	   *((vuint8*)IDM_AR0) = (alt_u8)((addr & 0xFF00) >> 8);
	   *((vuint8*)IDM_AR1) = (alt_u8)(addr & 0x00FF);
	   for (idx = 0; idx < len ; idx++) *((vuint8*)IDM_DR) = buf[idx];
	   IINCHIP_ISR_ENABLE();
#elif (__DEF_IINCHIP_BUS__ == __DEF_IINCHIP_SPI_MODE__)
	alt_u16 idx = 0;
	
//	   IINCHIP_ISR_DISABLE();
//	IINCHIP_SpiInit();
      
	//SPI MODE I/F
	for(idx=0;idx<len;idx++)
	   {
		/*IINCHIP_CSoff();                             // CS=0, SPI start

		IINCHIP_SpiSendData(0xF0);
		IINCHIP_SpiSendData(((addr+idx) & 0xFF00) >> 8);
		IINCHIP_SpiSendData((addr+idx) & 0x00FF);
		IINCHIP_SpiSendData(buf[idx]);

		IINCHIP_CSon();                             // CS=0, SPI end */

		wiz( WRITE, (addr+idx), buf[idx] );
	   }

//	   IINCHIP_ISR_ENABLE();
#else
	#error "unknown bus type"
#endif
	return len;
}


/**
@brief	This function reads into W5100 memory(Buffer)
*/ 
alt_u16 wiz_read_buf(alt_u16 addr, alt_u8* buf,alt_u16 len)
{
#if (__DEF_IINCHIP_BUS__ == __DEF_IINCHIP_DIRECT_MODE__)
	IINCHIP_ISR_DISABLE();
	memcpy(buf, (alt_u8 *)addr, len);
	IINCHIP_ISR_ENABLE();
#elif(__DEF_IINCHIP_BUS__ == __DEF_IINCHIP_INDIRECT_MODE__)
	   alt_u16 idx = 0;
	   IINCHIP_ISR_DISABLE();
	   *((vuint8*)IDM_AR0) = (alt_u8)((addr & 0xFF00) >> 8);
	   *((vuint8*)IDM_AR1) = (alt_u8)(addr & 0x00FF);
	   for (idx = 0; idx < len ; idx++) buf[idx] = *((vuint8*)IDM_DR);
	   IINCHIP_ISR_ENABLE();
#elif (__DEF_IINCHIP_BUS__ == __DEF_IINCHIP_SPI_MODE__)
	alt_u16 idx = 0;
	   IINCHIP_ISR_DISABLE();

//	IINCHIP_SpiInit();
   
	for (idx=0; idx<len; idx++)
      	   {
		/*IINCHIP_CSoff();                             // CS=0, SPI start

		IINCHIP_SpiSendData(0x0F);
		IINCHIP_SpiSendData(((addr+idx) & 0xFF00) >> 8);
		IINCHIP_SpiSendData((addr+idx) & 0x00FF);


		IINCHIP_SpiSendData(0);
		buf[idx] = IINCHIP_SpiRecvData();

		IINCHIP_CSon();                             // CS=0, SPI end 	   
		*/
		buf[idx] = wiz( READ, (addr+idx), 0 );
	   }

//	   IINCHIP_ISR_ENABLE();
#else
	#error "unknown bus type"
#endif
	return len;
}


/**
@brief	Socket interrupt routine
*/ 
ISR(INT4_vect)
{
#ifdef __DEF_IINCHIP_INT__
	alt_u8 int_val;
	IINCHIP_ISR_DISABLE();
	int_val = IINCHIP_READ(IR);
	
	/* +200801[bj] process all of interupt */
   do {
   /*---*/
   
   	if (int_val & IR_CONFLICT)
   	{
   		printf("IP conflict : %.2x\r\n", int_val);
   	}
   	if (int_val & IR_UNREACH)
   	{
   		printf("INT Port Unreachable : %.2x\r\n", int_val);
   		printf("UIPR0 : %d.%d.%d.%d\r\n", IINCHIP_READ(UIPR0), IINCHIP_READ(UIPR0+1), IINCHIP_READ(UIPR0+2), IINCHIP_READ(UIPR0+3));
   		printf("UPORT0 : %.2x %.2x\r\n", IINCHIP_READ(UPORT0), IINCHIP_READ(UPORT0+1));
   	}
   
   	/* +200801[bj] interrupt clear */
   	IINCHIP_WRITE(IR, 0xf0); 
      /*---*/
   
   	if (int_val & IR_SOCK(0))
   	{
   	/* +-200801[bj] save interrupt value*/
   		I_STATUS[0] |= IINCHIP_READ(Sn_IR(0)); // can be come to over two times.
   		IINCHIP_WRITE(Sn_IR(0), I_STATUS[0]);
      /*---*/
   	}
   	if (int_val & IR_SOCK(1))
   	{
   	/* +-200801[bj] save interrupt value*/
   		I_STATUS[1] |= IINCHIP_READ(Sn_IR(1));
   		IINCHIP_WRITE(Sn_IR(1), I_STATUS[1]);
      /*---*/
   	}
   	if (int_val & IR_SOCK(2))
   	{
   	/* +-200801[bj] save interrupt value*/
   		I_STATUS[2] |= IINCHIP_READ(Sn_IR(2));
   		IINCHIP_WRITE(Sn_IR(2), I_STATUS[2]);
      /*---*/
   	}
   	if (int_val & IR_SOCK(3))
   	{
   	/* +-200801[bj] save interrupt value*/
   		I_STATUS[3] |= IINCHIP_READ(Sn_IR(3));
   		IINCHIP_WRITE(Sn_IR(3), I_STATUS[3]);
      /*---*/
   	}
   
   	/* +-200801[bj] re-read interrupt value*/
   	int_val = IINCHIP_READ(IR);

	/* +200801[bj] if exist, contiue to process */
   } while (int_val != 0x00);
   /*---*/

	IINCHIP_ISR_ENABLE();
	
#endif

}

/**
@brief	This function is for resetting of the iinchip. Initializes the iinchip to work in whether DIRECT or INDIRECT mode
*/ 
void iinchip_init(void)
{	
	setMR( MR_RST );
#if (__DEF_IINCHIP_BUS__ == __DEF_IINCHIP_INDIRECT_MODE__)
	setMR( MR_IND | MR_AI );
#ifdef __DEF_IINCHIP_DBG__	
	printf("MR value is %d \r\n",IINCHIP_READ(MR));
#endif	
#endif
}


/**
@brief	This function set the transmit & receive buffer size as per the channels is used

Note for TMSR and RMSR bits are as follows\n
bit 1-0 : memory size of channel #0 \n
bit 3-2 : memory size of channel #1 \n
bit 5-4 : memory size of channel #2 \n
bit 7-6 : memory size of channel #3 \n\n
Maximum memory size for Tx, Rx in the W5100 is 8K Bytes,\n
In the range of 8KBytes, the memory size could be allocated dynamically by each channel.\n
Be attentive to sum of memory size shouldn't exceed 8Kbytes\n
and to data transmission and receiption from non-allocated channel may cause some problems.\n
If the 8KBytes memory is already  assigned to centain channel, \n
other 3 channels couldn't be used, for there's no available memory.\n
If two 4KBytes memory are assigned to two each channels, \n
other 2 channels couldn't be used, for there's no available memory.\n
*/ 
void sysinit(
	alt_u8 tx_size, 	/**< tx_size Tx memory size (00 - 1KByte, 01- 2KBtye, 10 - 4KByte, 11 - 8KByte) */
	alt_u8 rx_size		/**< rx_size Rx memory size (00 - 1KByte, 01- 2KBtye, 10 - 4KByte, 11 - 8KByte) */
	)
{
	alt_16 i;
	alt_16 ssum,rsum;

#ifdef __DEF_IINCHIP_DBG__
	printf("sysinit()\r\n");
#endif

	ssum = 0;
	rsum = 0;
	
	IINCHIP_WRITE(TMSR,tx_size); /* Set Tx memory size for each channel */
	IINCHIP_WRITE(RMSR,rx_size);	 /* Set Rx memory size for each channel */

	SBUFBASEADDRESS[0] = (alt_u16)(__DEF_IINCHIP_MAP_TXBUF__);		/* Set base address of Tx memory for channel #0 */
	RBUFBASEADDRESS[0] = (alt_u16)(__DEF_IINCHIP_MAP_RXBUF__);		/* Set base address of Rx memory for channel #0 */

#ifdef __DEF_IINCHIP_DBG__
	printf("Channel : SEND MEM SIZE : RECV MEM SIZE\r\n");
#endif

   for (i = 0 ; i < MAX_SOCK_NUM; i++)       // Set the size, masking and base address of Tx & Rx memory by each channel
	{
		SSIZE[i] = (alt_16)(0);
		RSIZE[i] = (alt_16)(0);
		if (ssum < 8192)
		{
         switch((tx_size >> i*2) & 0x03)  // Set Tx memory size
			{
			case 0:
				SSIZE[i] = (alt_16)(1024);
				SMASK[i] = (alt_u16)(0x03FF);
				break;
			case 1:
				SSIZE[i] = (alt_16)(2048);
				SMASK[i] = (alt_u16)(0x07FF);
				break;
			case 2:
				SSIZE[i] = (alt_16)(4096);
				SMASK[i] = (alt_u16)(0x0FFF);
				break;
			case 3:
				SSIZE[i] = (alt_16)(8192);
				SMASK[i] = (alt_u16)(0x1FFF);
				break;
			}
		}
		if (rsum < 8192)
		{
         switch((rx_size >> i*2) & 0x03)     // Set Rx memory size
			{
			case 0:
				RSIZE[i] = (alt_16)(1024);
				RMASK[i] = (alt_u16)(0x03FF);
				break;
			case 1:
				RSIZE[i] = (alt_16)(2048);
				RMASK[i] = (alt_u16)(0x07FF);
				break;
			case 2:
				RSIZE[i] = (alt_16)(4096);
				RMASK[i] = (alt_u16)(0x0FFF);
				break;
			case 3:
				RSIZE[i] = (alt_16)(8192);
				RMASK[i] = (alt_u16)(0x1FFF);
				break;
			}
		}
		ssum += SSIZE[i];
		rsum += RSIZE[i];

      if (i != 0)             // Sets base address of Tx and Rx memory for channel #1,#2,#3
		{
			SBUFBASEADDRESS[i] = SBUFBASEADDRESS[i-1] + SSIZE[i-1];
			RBUFBASEADDRESS[i] = RBUFBASEADDRESS[i-1] + RSIZE[i-1];
		}
#ifdef __DEF_IINCHIP_DBG__
		printf("%d : %.4x : %.4x : %.4x : %.4x\r\n", i, (alt_u16)SBUFBASEADDRESS[i], (alt_u16)RBUFBASEADDRESS[i], SSIZE[i], RSIZE[i]);
#endif
	}
}


void setMR(alt_u8 val)
{

#if (__DEF_IINCHIP_BUS__ == __DEF_IINCHIP_INDIRECT_MODE__)
	*((volatile alt_u8*)(MR)) = val;
#else
	/* 	DIRECT ACCESS	*/
	IINCHIP_WRITE(MR,val);
#endif	
}


/**
@brief	This function sets up gateway IP address.
*/ 
void setGAR(
	alt_u8 * addr	/**< a pointer to a 4 -byte array responsible to set the Gateway IP address. */
	)
{
	IINCHIP_WRITE((GAR0 + 0),addr[0]);
	IINCHIP_WRITE((GAR0 + 1),addr[1]);
	IINCHIP_WRITE((GAR0 + 2),addr[2]);
	IINCHIP_WRITE((GAR0 + 3),addr[3]);
}

void getGWIP(alt_u8 * addr)
{
	addr[0] = IINCHIP_READ((GAR0 + 0));
	addr[1] = IINCHIP_READ((GAR0 + 1));
	addr[2] = IINCHIP_READ((GAR0 + 2));
	addr[3] = IINCHIP_READ((GAR0 + 3));
}


/**
@brief	It sets up SubnetMask address
*/ 
void setSUBR(
	alt_u8 * addr	/**< a pointer to a 4 -byte array responsible to set the SubnetMask address */
	)
{
  SUBN_VAR[0] = addr[0];
	SUBN_VAR[1] = addr[1];
	SUBN_VAR[2] = addr[2];
	SUBN_VAR[3] = addr[3];
}


/**
@brief	It sets up SubnetMask address
*/ 
void applySUBR(
	void	/**< a pointer to a 4 -byte array responsible to set the SubnetMask address */
	)
{
	IINCHIP_WRITE((SUBR0 + 0), SUBN_VAR[0]);
	IINCHIP_WRITE((SUBR0 + 1), SUBN_VAR[1]);
	IINCHIP_WRITE((SUBR0 + 2), SUBN_VAR[2]);
	IINCHIP_WRITE((SUBR0 + 3), SUBN_VAR[3]);
}


/**
@brief	It sets up SubnetMask address
*/ 
void clearSUBR(
	void	/**< a pointer to a 4 -byte array responsible to set the SubnetMask address */
	)
{
	IINCHIP_WRITE((SUBR0 + 0), 0);
	IINCHIP_WRITE((SUBR0 + 1), 0);
	IINCHIP_WRITE((SUBR0 + 2), 0);
	IINCHIP_WRITE((SUBR0 + 3), 0);
}


/**
@brief	This function sets up MAC address.
*/ 
void setSHAR(
	alt_u8 * addr	/**< a pointer to a 6 -byte array responsible to set the MAC address. */
	)
{
	IINCHIP_WRITE((SHAR0 + 0),addr[0]);
	IINCHIP_WRITE((SHAR0 + 1),addr[1]);
	IINCHIP_WRITE((SHAR0 + 2),addr[2]);
	IINCHIP_WRITE((SHAR0 + 3),addr[3]);
	IINCHIP_WRITE((SHAR0 + 4),addr[4]);
	IINCHIP_WRITE((SHAR0 + 5),addr[5]);
}


/**
@brief	This function sets up Source IP address.
*/
void setSIPR(
	alt_u8 * addr	/**< a pointer to a 4 -byte array responsible to set the Source IP address. */
	)
{
	IINCHIP_WRITE((SIPR0 + 0),addr[0]);
	IINCHIP_WRITE((SIPR0 + 1),addr[1]);
	IINCHIP_WRITE((SIPR0 + 2),addr[2]);
	IINCHIP_WRITE((SIPR0 + 3),addr[3]);
}


/**
@brief	This function gets Interrupt register in common register.
 */
alt_u8 getIR( void )
{
   return IINCHIP_READ(IR);
}



/**
@brief	This function sets up Retransmission time.

If there is no response from the peer or delay in response then retransmission 
will be there as per RTR (Retry Time-value Register)setting
*/
void setRTR(alt_u16 timeout)
{
	IINCHIP_WRITE(RTR0,(alt_u8)((timeout & 0xff00) >> 8));
	IINCHIP_WRITE((RTR0 + 1),(alt_u8)(timeout & 0x00ff));
}


/**
@brief	This function set the number of Retransmission.

If there is no response from the peer or delay in response then recorded time 
as per RTR & RCR register seeting then time out will occur.
*/
void setRCR(alt_u8 retry)
{
	IINCHIP_WRITE(RCR,retry);
}


/**
@brief	This function set the interrupt mask Enable/Disable appropriate Interrupt. ('1' : interrupt enable)

If any bit in IMR is set as '0' then there is not interrupt signal though the bit is
set in IR register.
*/
void setIMR(alt_u8 mask)
{
	IINCHIP_WRITE(IMR,mask); // must be setted 0x10.
}


/**
@brief	These below functions are used to get the Gateway, SubnetMask
		and Source Hardware Address (MAC Address) and Source IP address
*/
void getGAR(alt_u8 * addr)
{
	addr[0] = IINCHIP_READ(GAR0);
	addr[1] = IINCHIP_READ(GAR0+1);
	addr[2] = IINCHIP_READ(GAR0+2);
	addr[3] = IINCHIP_READ(GAR0+3);
}
void getSUBR(alt_u8 * addr)
{
	addr[0] = SUBN_VAR[0];
	addr[1] = SUBN_VAR[1];
	addr[2] = SUBN_VAR[2];
	addr[3] = SUBN_VAR[3];
}
void getSHAR(alt_u8 * addr)
{
	addr[0] = IINCHIP_READ(SHAR0);
	addr[1] = IINCHIP_READ(SHAR0+1);
	addr[2] = IINCHIP_READ(SHAR0+2);
	addr[3] = IINCHIP_READ(SHAR0+3);
	addr[4] = IINCHIP_READ(SHAR0+4);
	addr[5] = IINCHIP_READ(SHAR0+5);
}
void getSIPR(alt_u8 * addr)
{
	addr[0] = IINCHIP_READ(SIPR0);
	addr[1] = IINCHIP_READ(SIPR0+1);
	addr[2] = IINCHIP_READ(SIPR0+2);
	addr[3] = IINCHIP_READ(SIPR0+3);
}


/**
@brief	These below functions are used to get the Destination Hardware Address (MAC Address), Destination IP address and Destination Port.
*/
void getSn_DHAR(SOCKET s, alt_u8 * addr)
{
	addr[0] = IINCHIP_READ(Sn_DHAR0(s));
	addr[1] = IINCHIP_READ(Sn_DHAR0(s)+1);
	addr[2] = IINCHIP_READ(Sn_DHAR0(s)+2);
	addr[3] = IINCHIP_READ(Sn_DHAR0(s)+3);
	addr[4] = IINCHIP_READ(Sn_DHAR0(s)+4);
	addr[5] = IINCHIP_READ(Sn_DHAR0(s)+5);
}
void setSn_DHAR(SOCKET s, alt_u8 * addr)
{
	IINCHIP_WRITE((Sn_DHAR0(s) + 0),addr[0]);
	IINCHIP_WRITE((Sn_DHAR0(s) + 1),addr[1]);
	IINCHIP_WRITE((Sn_DHAR0(s) + 2),addr[2]);
	IINCHIP_WRITE((Sn_DHAR0(s) + 3),addr[3]);
	IINCHIP_WRITE((Sn_DHAR0(s) + 4),addr[4]);
	IINCHIP_WRITE((Sn_DHAR0(s) + 5),addr[5]);
}
void getSn_DIPR(SOCKET s, alt_u8 * addr)
{
	addr[0] = IINCHIP_READ(Sn_DIPR0(s));
	addr[1] = IINCHIP_READ(Sn_DIPR0(s)+1);
	addr[2] = IINCHIP_READ(Sn_DIPR0(s)+2);
	addr[3] = IINCHIP_READ(Sn_DIPR0(s)+3);
}
void setSn_DIPR(SOCKET s, alt_u8 * addr)
{
	IINCHIP_WRITE((Sn_DIPR0(s) + 0),addr[0]);
	IINCHIP_WRITE((Sn_DIPR0(s) + 1),addr[1]);
	IINCHIP_WRITE((Sn_DIPR0(s) + 2),addr[2]);
	IINCHIP_WRITE((Sn_DIPR0(s) + 3),addr[3]);
}
void getSn_DPORT(SOCKET s, alt_u8 * addr)
{
	addr[0] = IINCHIP_READ(Sn_DPORT0(s));
	addr[1] = IINCHIP_READ(Sn_DPORT0(s)+1);
}
void setSn_DPORT(SOCKET s, alt_u8 * addr)
{
	IINCHIP_WRITE((Sn_DPORT0(s) + 0),addr[0]);
	IINCHIP_WRITE((Sn_DPORT0(s) + 1),addr[1]);
}


/**
@brief	This sets the maximum segment size of TCP in Active Mode), while in Passive Mode this is set by peer
*/
void setSn_MSS(SOCKET s, alt_u16 Sn_MSSR0)
{
	IINCHIP_WRITE(Sn_MSSR0(s),(alt_u8)((Sn_MSSR0 & 0xff00) >> 8));
	IINCHIP_WRITE((Sn_MSSR0(s) + 1),(alt_u8)(Sn_MSSR0 & 0x00ff));
}

void setSn_TTL(SOCKET s, alt_u8 ttl)
{
   IINCHIP_WRITE(Sn_TTL(s), ttl);
}


/**
@brief	These below function is used to setup the Protocol Field of IP Header when
		executing the IP Layer RAW mode.
*/
void setSn_PROTO(SOCKET s, alt_u8 proto)
{
	IINCHIP_WRITE(Sn_PROTO(s),proto);
}


/**
@brief	get socket interrupt status

These below functions are used to read the Interrupt & Soket Status register
*/
alt_u8 getSn_IR(SOCKET s)
{
   return IINCHIP_READ(Sn_IR(s));
}

void setSn_IR(SOCKET s, alt_u8 value)
{
	IINCHIP_WRITE(Sn_IR(s), value);
}


/**
@brief	 get socket status
*/
alt_u8 getSn_SR(SOCKET s)
{
   return IINCHIP_READ(Sn_SR(s));
}


/**
@brief	get socket TX free buf size

This gives free buffer size of transmit buffer. This is the data size that user can transmit.
User shuold check this value first and control the size of transmitting data
*/
alt_u16 getSn_TX_FSR(SOCKET s)
{
	alt_u16 val=0,val1=0;
	do
	{
		val1 = IINCHIP_READ(Sn_TX_FSR0(s));
		val1 = (val1 << 8) + IINCHIP_READ(Sn_TX_FSR0(s) + 1);
    if (val1 != 0)
		{
   			val = IINCHIP_READ(Sn_TX_FSR0(s));
   			val = (val << 8) + IINCHIP_READ(Sn_TX_FSR0(s) + 1);
		}
	} while (val != val1);
  return val;
}


/**
@brief	 get socket RX recv buf size

This gives size of received data in receive buffer. 
*/
alt_u16 getSn_RX_RSR(SOCKET s)
{
	alt_u16 val=0,val1=0;
	do
	{
		val1 = IINCHIP_READ(Sn_RX_RSR0(s));
		val1 = (val1 << 8) + IINCHIP_READ(Sn_RX_RSR0(s) + 1);
      if(val1 != 0)
		{
   			val = IINCHIP_READ(Sn_RX_RSR0(s));
   			val = (val << 8) + IINCHIP_READ(Sn_RX_RSR0(s) + 1);
		}
	} while (val != val1);
   return val;
}


/**
@brief	 This function is being called by send() and sendto() function also. 

This function read the Tx write pointer register and after copy the data in buffer update the Tx write pointer
register. User should read upper byte first and lower byte later to get proper value.
*/
void send_data_processing(SOCKET s, alt_u8 *data, alt_u16 len)
{
	alt_u16 ptr;
	ptr = IINCHIP_READ(Sn_TX_WR0(s));
	ptr = ((ptr & 0x00ff) << 8) + IINCHIP_READ(Sn_TX_WR0(s) + 1);
	write_data(s, data, (alt_u8 *)(ptr), len);
	ptr += len;
	IINCHIP_WRITE(Sn_TX_WR0(s),(alt_u8)((ptr & 0xff00) >> 8));
	IINCHIP_WRITE((Sn_TX_WR0(s) + 1),(alt_u8)(ptr & 0x00ff));
}


/**
@brief	This function is being called by recv() also.

This function read the Rx read pointer register
and after copy the data from receive buffer update the Rx write pointer register.
User should read upper byte first and lower byte later to get proper value.
*/
void recv_data_processing(SOCKET s, alt_u8 *data, alt_u16 len)
{
	alt_u16 ptr;
	ptr = IINCHIP_READ(Sn_RX_RD0(s));
	ptr = ((ptr & 0x00ff) << 8) + IINCHIP_READ(Sn_RX_RD0(s) + 1);
#ifdef __DEF_IINCHIP_DBG__
	printf("ISR_RX: rd_ptr : %.4x\r\n", ptr);
#endif
	read_data(s, (alt_u8 *)ptr, data, len); // read data
	ptr += len;
	IINCHIP_WRITE(Sn_RX_RD0(s),(alt_u8)((ptr & 0xff00) >> 8));
	IINCHIP_WRITE((Sn_RX_RD0(s) + 1),(alt_u8)(ptr & 0x00ff));
}


/**
@brief	for copy the data form application buffer to Transmite buffer of the chip.

This function is being used for copy the data form application buffer to Transmite
buffer of the chip. It calculate the actual physical address where one has to write
the data in transmite buffer. Here also take care of the condition while it exceed
the Tx memory uper-bound of socket.
*/
void write_data(SOCKET s, valt_u8 * src, valt_u8 * dst, alt_u16 len)
{
	alt_u16 size;
	alt_u16 dst_mask;
	alt_u8 * dst_ptr;

	dst_mask = (alt_u16)dst & getIINCHIP_TxMASK(s);
	dst_ptr = (alt_u8 *)(getIINCHIP_TxBASE(s) + dst_mask);
	
	if (dst_mask + len > getIINCHIP_TxMAX(s)) 
	{
		size = getIINCHIP_TxMAX(s) - dst_mask;
		wiz_write_buf((alt_u16)dst_ptr, (alt_u8*)src, size);
		src += size;
		size = len - size;
		dst_ptr = (alt_u8 *)(getIINCHIP_TxBASE(s));
		wiz_write_buf((alt_u16)dst_ptr, (alt_u8*)src, size);
	} 
	else
	{
		wiz_write_buf((alt_u16)dst_ptr, (alt_u8*)src, len);
	}
}


/**
@brief	This function is being used for copy the data form Receive buffer of the chip to application buffer.

It calculate the actual physical address where one has to read
the data from Receive buffer. Here also take care of the condition while it exceed
the Rx memory uper-bound of socket.
*/
void read_data(SOCKET s, valt_u8 * src, valt_u8 * dst, alt_u16 len)
{
	alt_u16 size;
	alt_u16 src_mask;
	alt_u8 * src_ptr;

	src_mask = (alt_u16)src & getIINCHIP_RxMASK(s);
	src_ptr = (alt_u8 *)(getIINCHIP_RxBASE(s) + src_mask);
	
	if( (src_mask + len) > getIINCHIP_RxMAX(s) ) 
	{
		size = getIINCHIP_RxMAX(s) - src_mask;
		wiz_read_buf((alt_u16)src_ptr, (alt_u8*)dst,size);
		dst += size;
		size = len - size;
		src_ptr = (alt_u8 *)(getIINCHIP_RxBASE(s));
		wiz_read_buf((alt_u16)src_ptr, (alt_u8*) dst,size);
	} 
	else
	{
		wiz_read_buf((alt_u16)src_ptr, (alt_u8*) dst,len);
	}
}


#ifdef __DEF_IINCHIP_PPP__
#define PPP_OPTION_BUF_LEN 64

alt_u8 pppinit_in(alt_u8 * id, alt_u8 idlen, alt_u8 * passwd, alt_u8 passwdlen);


/**
@brief	make PPPoE connection
@return	1 => success to connect, 2 => Auth fail, 3 => timeout, 4 => Auth type not support

*/
alt_u8 pppinit(alt_u8 * id, alt_u8 idlen, alt_u8 * passwd, alt_u8 passwdlen)
{
	alt_u8 ret;
	alt_u8 isr;
	
	// PHASE0. W5100 PPPoE(ADSL) setup
	// enable pppoe mode
	printf("-- PHASE 0. W5100 PPPoE(ADSL) setup process --\r\n");
	printf("\r\n");
	IINCHIP_WRITE(MR,IINCHIP_READ(MR) | MR_PPPOE);

	// open socket in pppoe mode
	isr = IINCHIP_READ(Sn_IR(0));// first clear isr(0), W5100 at present time
	IINCHIP_WRITE(Sn_IR(0),isr);
	
	IINCHIP_WRITE(PTIMER,200); // 5sec timeout
	IINCHIP_WRITE(PMAGIC,0x01); // magic number
	IINCHIP_WRITE(Sn_MR(0),Sn_MR_PPPOE);
	IINCHIP_WRITE(Sn_CR(0),Sn_CR_OPEN);
	
	/* +20071122[chungs]:wait to process the command... */
	while( IINCHIP_READ(Sn_CR(0)) ) 
		;
	/* ------- */
	
	ret = pppinit_in(id, idlen, passwd, passwdlen);

	// close ppp connection socket
	/* +200801 (hwkim) */
	close(0);
	/* ------- */
	
	return ret;
}


alt_u8 pppinit_in(alt_u8 * id, alt_u8 idlen, alt_u8 * passwd, alt_u8 passwdlen)
{
	alt_u8 i = 0;
	alt_u8 loop_idx = 0;
	alt_u8 isr = 0;
	alt_u8 buf[PPP_OPTION_BUF_LEN];
	alt_u16 len;
	alt_u8 str[PPP_OPTION_BUF_LEN];
	alt_u8 str_idx,dst_idx;

   // PHASE1. PPPoE Discovery
	// start to connect pppoe connection
	printf("-- PHASE 1. PPPoE Discovery process --");
	printf(" ok\r\n");
	printf("\r\n");
	IINCHIP_WRITE(Sn_CR(0),Sn_CR_PCON);
	/* +20071122[chungs]:wait to process the command... */
	while( IINCHIP_READ(Sn_CR(0)) ) 
		;
	/* ------- */
	
	wait_10ms(100);

	loop_idx = 0;
	//check whether PPPoE discovery end or not
	while (!(IINCHIP_READ(Sn_IR(0)) & Sn_IR_PNEXT))
	{
		printf(".");
		if (loop_idx++ == 10) // timeout
		{
			printf("timeout before LCP\r\n"); 
			return 3;
		}
		wait_10ms(100);
	}

   /* +200801[bj] clear interrupt value*/
   IINCHIP_WRITE(Sn_IR(0), 0xff);
   /*---*/

   // PHASE2. LCP process
	printf("-- PHASE 2. LCP process --");
		
	// send LCP Request
	{
		// Magic number option
		// option format (type value + length value + data)
	   // write magic number value
		buf[0] = 0x05; // type value
		buf[1] = 0x06; // length value
		buf[2] = 0x01; buf[3] = 0x01; buf[4] = 0x01; buf[5]= 0x01; // data
		// for MRU option, 1492 0x05d4  
		// buf[6] = 0x01; buf[7] = 0x04; buf[8] = 0x05; buf[9] = 0xD4;
	}
	send_data_processing(0, buf, 0x06);
	IINCHIP_WRITE(Sn_CR(0),Sn_CR_PCR); // send request 
	/* +20071122[chungs]:wait to process the command... */
	while( IINCHIP_READ(Sn_CR(0)) ) 
		;
	/* ------- */
		
	wait_10ms(100);

	while (!((isr = IINCHIP_READ(Sn_IR(0))) & Sn_IR_PNEXT))
	{
		if (isr & Sn_IR_PRECV) // Not support option
		{
   /* +200801[bj] clear interrupt value*/
         IINCHIP_WRITE(Sn_IR(0), Sn_IR_PRECV);
   /*---*/
			len = getSn_RX_RSR(0);
			if ( len > 0 )
			{
				recv_data_processing(0, str, len);
				IINCHIP_WRITE(Sn_CR(0),Sn_CR_RECV);
				/* +20071122[chungs]:wait to process the command... */
				while( IINCHIP_READ(Sn_CR(0)) ) 
					;
				/* ------- */
				
				// for debug
				//printf("LCP proc len = %d\r\n", len); for (i = 0; i < len; i++) printf ("%02x ", str[i]); printf("\r\n");
				// get option length
				len = str[4]; len = ((len & 0x00ff) << 8) + str[5];
				len += 2;
				str_idx = 6; dst_idx = 0; // ppp header is 6 byte, so starts at 6.
				do 
				{
					if ((str[str_idx] == 0x01) || (str[str_idx] == 0x02) || (str[str_idx] == 0x03) || (str[str_idx] == 0x05))
					{
						// skip as length of support option. str_idx+1 is option's length.
						str_idx += str[str_idx+1];
					}
					else
					{
						// not support option , REJECT
						memcpy((alt_u8 *)(buf+dst_idx), (alt_u8 *)(str+str_idx), str[str_idx+1]);
						dst_idx += str[str_idx+1]; str_idx += str[str_idx+1];
					}
				} while (str_idx != len);
	   			// for debug
	   			//printf("LCP dst proc\r\n"); for (i = 0; i < dst_idx; i++) printf ("%02x ", buf[i]); printf("\r\n");
	   
	   			// send LCP REJECT packet
	   			send_data_processing(0, buf, dst_idx);
	   			IINCHIP_WRITE(Sn_CR(0),Sn_CR_PCJ);
				/* +20071122[chungs]:wait to process the command... */
				while( IINCHIP_READ(Sn_CR(0)) ) 
					;
				/* ------- */
  			}
		}
		printf(".");
		if (loop_idx++ == 10) // timeout
		{
			printf("timeout after LCP\r\n");
			return 3;
		}
		wait_10ms(100);
	}
	printf(" ok\r\n");
	printf("\r\n");

   /* +200801[bj] clear interrupt value*/
   IINCHIP_WRITE(Sn_IR(0), 0xff);

   /* +200903[bj] clear receive buffer */
	len = getSn_RX_RSR(0);
	if ( len > 0 )
	{
		recv_data_processing(0, str, len);
		//printf("dummy proc len = %d\r\n", len); for (i = 0; i < len; i++) printf ("%02x ", str[i]); printf("\r\n");
		IINCHIP_WRITE(Sn_CR(0),Sn_CR_RECV);
		while( IINCHIP_READ(Sn_CR(0)) ) 
			;
	}
   /*---*/

	printf("-- PHASE 3. PPPoE(ADSL) Authentication mode --\r\n");
	printf("Authentication protocol : %.2x %.2x, ", IINCHIP_READ(PATR0), IINCHIP_READ(PATR0+1));

	loop_idx = 0;
	if (IINCHIP_READ(PATR0) == 0xc0 && IINCHIP_READ(PATR0+1) == 0x23)
	{
		printf("PAP\r\n"); // in case of adsl normally supports PAP.
		// send authentication data
		// copy (idlen + id + passwdlen + passwd)
		buf[loop_idx] = idlen; loop_idx++;
		memcpy((alt_u8 *)(buf+loop_idx), (alt_u8 *)(id), idlen); loop_idx += idlen;
		buf[loop_idx] = passwdlen; loop_idx++;
		memcpy((alt_u8 *)(buf+loop_idx), (alt_u8 *)(passwd), passwdlen); loop_idx += passwdlen;
		send_data_processing(0, buf, loop_idx);
		IINCHIP_WRITE(Sn_CR(0),Sn_CR_PCR);
		/* +20071122[chungs]:wait to process the command... */
		while( IINCHIP_READ(Sn_CR(0)) ) 
			;
		/* ------- */
		wait_10ms(100);
	}	
	else if (IINCHIP_READ(PATR0) == 0xc2 && IINCHIP_READ(PATR0+1) == 0x23)
	{
		alt_u8 chal_len;
		md5_ctx context;
		alt_u8  digest[16];

		len = getSn_RX_RSR(0);
		if ( len > 0 )
		{
			recv_data_processing(0, str, len);
			IINCHIP_WRITE(Sn_CR(0),Sn_CR_RECV);
			/* +20071122[chungs]:wait to process the command... */
			while( IINCHIP_READ(Sn_CR(0)) ) 
				;
			/* ------- */
#ifdef __DEF_IINCHIP_DBG__
			printf("recv CHAP\r\n");
			{
				alt_16 i;
				
				for (i = 0; i < 32; i++) 
					printf ("%02x ", str[i]);
			}
			printf("\r\n");
#endif
// str is C2 23 xx CHAL_ID xx xx CHAP_LEN CHAP_DATA
// index  0  1  2  3       4  5  6        7 ...

			memset(buf,0x00,64);
			buf[loop_idx] = str[3]; loop_idx++; // chal_id
			memcpy((alt_u8 *)(buf+loop_idx), (alt_u8 *)(passwd), passwdlen); loop_idx += passwdlen; //passwd
			chal_len = str[6]; // chal_id
			memcpy((alt_u8 *)(buf+loop_idx), (alt_u8 *)(str+7), chal_len); loop_idx += chal_len; //challenge
			buf[loop_idx] = 0x80;
#ifdef __DEF_IINCHIP_DBG__
			printf("CHAP proc d1\r\n");
			{
				alt_16 i;
				for (i = 0; i < 64; i++) 
					printf ("%02x ", buf[i]);
			}
			printf("\r\n");
#endif

			md5_init(&context);
			md5_update(&context, buf, loop_idx);
			md5_final(digest, &context);

#ifdef __DEF_IINCHIP_DBG__
			printf("CHAP proc d1\r\n");
			{
				alt_16 i;
				for (i = 0; i < 16; i++) 
					printf ("%02x", digest[i]);
			}
			printf("\r\n");
#endif
			loop_idx = 0;
			buf[loop_idx] = 16; loop_idx++; // hash_len
			memcpy((alt_u8 *)(buf+loop_idx), (alt_u8 *)(digest), 16); loop_idx += 16; // hashed value
			memcpy((alt_u8 *)(buf+loop_idx), (alt_u8 *)(id), idlen); loop_idx += idlen; // id
			send_data_processing(0, buf, loop_idx);
			IINCHIP_WRITE(Sn_CR(0),Sn_CR_PCR);
			/* +20071122[chungs]:wait to process the command... */
			while( IINCHIP_READ(Sn_CR(0)) ) 
				;
			/* ------- */
			wait_10ms(100);
		}
	}
	else
	{
		printf("Not support\r\n");
#ifdef __DEF_IINCHIP_DBG__
		printf("Not support PPP Auth type: %.2x%.2x\r\n",IINCHIP_READ(PATR0), IINCHIP_READ(PATR0+1));
#endif
		return 4;
	}
	printf("\r\n");

	printf("-- Waiting for PPPoE server's admission --");
	loop_idx = 0;
	while (!((isr = IINCHIP_READ(Sn_IR(0))) & Sn_IR_PNEXT))
	{
		if (isr & Sn_IR_PFAIL)
		{
   /* +200801[bj] clear interrupt value*/
   IINCHIP_WRITE(Sn_IR(0), 0xff);
   /*---*/
			printf("failed\r\nReinput id, password..\r\n");
			return 2;
		}
		printf(".");
		if (loop_idx++ == 10) // timeout
		{
   /* +200801[bj] clear interrupt value*/
   IINCHIP_WRITE(Sn_IR(0), 0xff);
   /*---*/
			printf("timeout after PAP\r\n");
			return 3;
		}
		wait_10ms(100);
	}
   /* +200801[bj] clear interrupt value*/
   IINCHIP_WRITE(Sn_IR(0), 0xff);

   /* +200903[bj] clear receive buffer */
	len = getSn_RX_RSR(0);
	if ( len > 0 )
	{
		recv_data_processing(0, str, len);
		//printf("dummy proc len = %d\r\n", len); for (i = 0; i < len; i++) printf ("%02x ", str[i]); printf("\r\n");
		IINCHIP_WRITE(Sn_CR(0),Sn_CR_RECV);
		while( IINCHIP_READ(Sn_CR(0)) ) 
			;
	}
   /*---*/

	printf("ok\r\n");
	printf("\r\n");
	printf("-- PHASE 4. IPCP process --");
	// IP Address
	buf[0] = 0x03; buf[1] = 0x06; buf[2] = 0x00; buf[3] = 0x00; buf[4] = 0x00; buf[5] = 0x00;
	send_data_processing(0, buf, 6);
	IINCHIP_WRITE(Sn_CR(0),Sn_CR_PCR);
	/* +20071122[chungs]:wait to process the command... */
	while( IINCHIP_READ(Sn_CR(0)) ) 
		;
	/* ------- */
	wait_10ms(100);

	loop_idx = 0;
	while (1)
	{
		if (IINCHIP_READ(Sn_IR(0)) & Sn_IR_PRECV)
		{
   /* +200801[bj] clear interrupt value*/
   IINCHIP_WRITE(Sn_IR(0), 0xff);
   /*---*/
			len = getSn_RX_RSR(0);
			if ( len > 0 )
			{
				recv_data_processing(0, str, len);
				IINCHIP_WRITE(Sn_CR(0),Sn_CR_RECV);
				/* +20071122[chungs]:wait to process the command... */
				while( IINCHIP_READ(Sn_CR(0)) ) 
					;
				/* ------- */
	   			//for debug
	   			//printf("IPCP proc len = %d\r\n", len); for (i = 0; i < len; i++) printf ("%02x ", str[i]); printf("\r\n");
	   			str_idx = 6; dst_idx = 0;
	   			if (str[2] == 0x03) // in case of NAK
	   			{
	   				do 
	   				{
	   					if (str[str_idx] == 0x03) // request only ip information
	   					{
	   						memcpy((alt_u8 *)(buf+dst_idx), (alt_u8 *)(str+str_idx), str[str_idx+1]);
	   						dst_idx += str[str_idx+1]; str_idx += str[str_idx+1];
	   					}
	   					else
	   					{
	   						// skip byte
	   						str_idx += str[str_idx+1];
	   					}
	   					// for debug
	   					//printf("s: %d, d: %d, l: %d", str_idx, dst_idx, len);
	   				} while (str_idx != len);
	   				send_data_processing(0, buf, dst_idx);
	   				IINCHIP_WRITE(Sn_CR(0),Sn_CR_PCR); // send ipcp request
	   				/* +20071122[chungs]:wait to process the command... */
					while( IINCHIP_READ(Sn_CR(0)) ) 
						;
					/* ------- */
	   				wait_10ms(100);
	   				break;
	   			}
			}
		}
		printf(".");
		if (loop_idx++ == 10) // timeout
		{
			printf("timeout after IPCP\r\n");
			return 3;
		}
		wait_10ms(100);
		send_data_processing(0, buf, 6);
		IINCHIP_WRITE(Sn_CR(0),Sn_CR_PCR); //ipcp re-request
		/* +20071122[chungs]:wait to process the command... */
		while( IINCHIP_READ(Sn_CR(0)) ) 
			;
		/* ------- */
	}

	loop_idx = 0;
	while (!(IINCHIP_READ(Sn_IR(0)) & Sn_IR_PNEXT))
	{
		printf(".");
		if (loop_idx++ == 10) // timeout
		{
			printf("timeout after IPCP NAK\r\n");
			return 3;
		}
		wait_10ms(100);
		IINCHIP_WRITE(Sn_CR(0),Sn_CR_PCR); // send ipcp request
		/* +20071122[chungs]:wait to process the command... */
		while( IINCHIP_READ(Sn_CR(0)) ) 
			;
		/* ------- */
	}
   /* +200801[bj] clear interrupt value*/
   IINCHIP_WRITE(Sn_IR(0), 0xff);
   /*---*/
	printf("ok\r\n");
	printf("\r\n");
	return 1;
	// after this function, User must save the pppoe server's mac address and pppoe session id in current connection
}


/**
@brief	terminate PPPoE connection
*/
alt_u8 pppterm(alt_u8 * mac, alt_u8 * sessionid)
{
	alt_u16 i;
	alt_u8 isr;
#ifdef __DEF_IINCHIP_DBG__
	printf("pppterm()\r\n");
#endif
	/* Set PPPoE bit in MR(Common Mode Register) : enable socket0 pppoe */
	IINCHIP_WRITE(MR,IINCHIP_READ(MR) | MR_PPPOE);
	
	// write pppoe server's mac address and session id 
	// must be setted these value.
	for (i = 0; i < 6; i++) IINCHIP_WRITE((Sn_DHAR0(0)+i),mac[i]);
	for (i = 0; i < 2; i++) IINCHIP_WRITE((Sn_DPORT0(0)+i),sessionid[i]);
	isr = IINCHIP_READ(Sn_IR(0));
	IINCHIP_WRITE(Sn_IR(0),isr);
	
	//open socket in pppoe mode
	IINCHIP_WRITE(Sn_MR(0),Sn_MR_PPPOE);
	IINCHIP_WRITE(Sn_CR(0),Sn_CR_OPEN);
	/* +20071122[chungs]:wait to process the command... */
	while( IINCHIP_READ(Sn_CR(0)) ) 
		;
	/* ------- */
	wait_1us(1);
	// close pppoe connection
	IINCHIP_WRITE(Sn_CR(0),Sn_CR_PDISCON);
	/* +20071122[chungs]:wait to process the command... */
	while( IINCHIP_READ(Sn_CR(0)) ) 
		;
	/* ------- */
	wait_10ms(100);
	// close socket
	/* +200801 (hwkim) */
	close(0);
	/* ------- */
	

#ifdef __DEF_IINCHIP_DBG__
	printf("pppterm() end ..\r\n");
#endif

	return 1;
}
#endif
